﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Net;

namespace SMS_Server
{
    public partial class ServiceMain : ServiceBase
    {
        Service_Controller sc;
        Message_Processor mp;
        Device_Controller dc;

        public ServiceMain()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            Thread th = new Thread(StartLoading);
            th.Start();
        }

        private void OnlyService()
        {
            sc = new Service_Controller();
            sc.StartServer();
        }

        private void StartLoading()
        {
            string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            if (path[path.Length - 1] == '\\') path = path.Substring(0, path.Length - 1);

            if (!System.IO.Directory.Exists(path+"\\Config"))
            {
                //new so exit and wait to be configured
                OnlyService();
                ServiceLog.WriteEntry("config missing: " + path + "\\Config", EventLogEntryType.Error);
                return;
            }
            if (!System.IO.Directory.Exists(path + "\\App_Data"))
            {
                //new so exit and wait to be configured
                OnlyService();
                ServiceLog.WriteEntry("data missing: " + path + "\\App_Data", EventLogEntryType.Error);
                return;
            }

            if (!System.IO.File.Exists(path + "\\config\\config.src"))
            {
                //new so exit and wait to be configured
                OnlyService();
                ServiceLog.WriteEntry("config.src missing: " + path + "\\config\\config.src", EventLogEntryType.Error);
                return;
            }

            //now Generate sdf and continue
            string ConfigFile = path + "\\App_Data\\Config.sdf";
            if (System.IO.File.Exists(ConfigFile))
            {
                try
                {
                    System.IO.File.Delete(ConfigFile);
                }
                catch (Exception ex)
                {
                    ServiceLog.WriteEntry(ex.Message, EventLogEntryType.Error);
                    return;
                }
            }

            try
            {
                FileStream Sourcefs = new FileStream(path + "\\config\\config.src", FileMode.Open, FileAccess.Read);
                FileStream Destinationfs = new FileStream(ConfigFile, FileMode.Create, FileAccess.Write);

                int counter = 0;
                long totall = Sourcefs.Length;
                long tillnow = 0;
                byte[] buffer = new byte[1024];


                while (true)
                {
                    counter = Sourcefs.Read(buffer, 0, buffer.Length);
                    if (counter == 0) break;

                    if (counter == buffer.Length)
                    {
                        Destinationfs.Write(buffer, 0, counter);
                    }
                    else
                    {
                        byte[] secbuff = new byte[counter];
                        for (int i = 0; i < counter; i++)
                        {
                            secbuff[i] = buffer[i];
                        }
                        Destinationfs.Write(secbuff, 0, counter);
                    }
                    tillnow += counter;
                }
                Destinationfs.Close();
                Sourcefs.Close();
            }
            catch(Exception ex)
            {
                ServiceLog.WriteEntry(ex.Message, EventLogEntryType.Error);
                //error generating cache
                return;
            }

            //lets load the functions and actions here
            DataAccess DA = new DataAccess(Global.GetConfigurationDatabaseName(), Global.DatabasePassword);
            DA.CommandString = "SELECT * FROM Functions";
            DA.ExecuteReader();
            while (DA.Reader.Read())
            {
                string FunctionName = DA.Reader.GetString(0);
                long FunctionID = DA.Reader.GetInt64(1);
                string ParamFormat = DA.Reader.GetString(2);
                Function function = new Function();
                function.FunctionName = FunctionName;
                function.ParamFormat = ParamFormat;
                function.Actions = new List<Incoming_Action>();
                Global.Tasks.Add(function.FunctionName, function);
            }
            DA.Reader.Close();

            int functioncount = 0;
            foreach (Function f in Global.Tasks.Values)
            {
                //Load static response actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_Static WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_StaticResponse aS = new Action_StaticResponse();
                    aS.ActionFormat = f.ParamFormat;
                    aS.ActionName = f.FunctionName;
                    aS.ResponseString = DA.Reader.GetString(2);
                    f.Actions.Add(aS);
                }
                DA.Reader.Close();
                //Load sms email actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_SMS_Email WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_SMS_Email se = new Action_SMS_Email();
                    se.ActionFormat = f.ParamFormat;
                    se.ActionName = f.FunctionName;
                    se.Address = DA.Reader.GetString(2);
                    string mode = DA.Reader.GetString(3);
                    if (mode.ToUpper() == "SMS") se.Mode = Action_SMS_Email.SendingMode.SMS; else se.Mode = Action_SMS_Email.SendingMode.EMAIL;
                    f.Actions.Add(se);
                }
                DA.Reader.Close();
                //Load toHTTP actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_ToHTTP WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Forward2HTTP th = new Action_Forward2HTTP();
                    th.ActionFormat = f.ParamFormat;
                    th.ActionName = f.FunctionName;
                    th.URL = DA.Reader.GetString(2);
                    string method = DA.Reader.GetString(3).ToUpper();
                    if (method == "GET") th.Method = Action_Forward2HTTP.HttpMetod.GET; else th.Method = Action_Forward2HTTP.HttpMetod.POST;
                    th.HasResponse = DA.Reader.GetBoolean(4);
                    f.Actions.Add(th);
                }
                DA.Reader.Close();
                //Load toTCP actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_ToTCP WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Forward2TCP tt = new Action_Forward2TCP();
                    tt.ActionFormat = f.ParamFormat;
                    tt.ActionName = f.FunctionName;
                    tt.IP = DA.Reader.GetString(2);
                    tt.Port = DA.Reader.GetInt32(3);
                    tt.HasResponse = DA.Reader.GetBoolean(4);
                    f.Actions.Add(tt);
                }
                DA.Reader.Close();
                //Load Search actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_Search WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Search sf = new Action_Search();
                    sf.ActionFormat = f.ParamFormat;
                    sf.ActionName = f.FunctionName;
                    string connector = DA.Reader.GetString(2).ToUpper();
                    if (connector == "OLEDB") sf.Connector = Action_Search.Connectors.OleDb; else if (connector == "ODBC") sf.Connector = Action_Search.Connectors.Odbc; else sf.Connector = Action_Search.Connectors.SQL;
                    sf.ConnectionString = DA.Reader.GetString(3);
                    sf.TableName = DA.Reader.GetString(4);
                    sf.SearchColumns = DA.Reader.GetString(5);
                    sf.ResponseColumns = DA.Reader.GetString(6);
                    sf.ResponseFormat = DA.Reader.GetString(7);
                    f.Actions.Add(sf);
                }
                DA.Reader.Close();
                //Load Insert actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_Insert WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", f.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Insert ii = new Action_Insert();
                    ii.ActionFormat = f.ParamFormat;
                    ii.ActionName = f.FunctionName;
                    string connector = DA.Reader.GetString(2).ToUpper();
                    if (connector == "OLEDB") ii.Connector = Action_Insert.Connectors.OleDb; else if (connector == "ODBC") ii.Connector = Action_Insert.Connectors.Odbc; else ii.Connector = Action_Insert.Connectors.SQL;
                    ii.ConnectionString = DA.Reader.GetString(3);
                    ii.TableName = DA.Reader.GetString(4);
                    ii.Columns = DA.Reader.GetString(5);
                    ii.HasResponse = DA.Reader.GetBoolean(6);
                    ii.ResponseColumns = DA.Reader.GetString(7);
                    ii.ResponseFormat = DA.Reader.GetString(8);
                    f.Actions.Add(ii);
                }
                DA.Reader.Close();
                functioncount++;
            }

            //Now load unknown
            bool unknownLoaded=false;
            if (!unknownLoaded)
            {
                Function function = new Function();
                function.FunctionName = "Unknown";
                function.ParamFormat = "";
                function.Actions = new List<Incoming_Action>();
                Global.Tasks.Add(function.FunctionName, function);

                //Load static response actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_Static WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", function.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_StaticResponse aS = new Action_StaticResponse();
                    aS.ActionFormat = function.ParamFormat;
                    aS.ActionName = function.FunctionName;
                    aS.ResponseString = DA.Reader.GetString(2);
                    function.Actions.Add(aS);
                }
                DA.Reader.Close();
                //Load sms email actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_SMS_Email WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", function.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_SMS_Email se = new Action_SMS_Email();
                    se.ActionFormat = function.ParamFormat;
                    se.ActionName = function.FunctionName;
                    se.Address = DA.Reader.GetString(2);
                    string mode = DA.Reader.GetString(3);
                    if (mode.ToUpper() == "SMS") se.Mode = Action_SMS_Email.SendingMode.SMS; else se.Mode = Action_SMS_Email.SendingMode.EMAIL;
                    function.Actions.Add(se);
                }
                DA.Reader.Close();
                //Load toHTTP actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_ToHTTP WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", function.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Forward2HTTP th = new Action_Forward2HTTP();
                    th.ActionFormat = function.ParamFormat;
                    th.ActionName = function.FunctionName;
                    th.URL = DA.Reader.GetString(2);
                    string method = DA.Reader.GetString(3).ToUpper();
                    if (method == "GET") th.Method = Action_Forward2HTTP.HttpMetod.GET; else th.Method = Action_Forward2HTTP.HttpMetod.POST;
                    th.HasResponse = DA.Reader.GetBoolean(4);
                    function.Actions.Add(th);
                }
                DA.Reader.Close();
                //Load toTCP actions
                DA.ClearParameters();
                DA.CommandString = "SELECT * FROM Action_ToTCP WHERE FunctionName=@FunctionName;";
                DA.AddParameter("@FunctionName", function.FunctionName);
                DA.ExecuteReader();
                while (DA.Reader.Read())
                {
                    Action_Forward2TCP tt = new Action_Forward2TCP();
                    tt.ActionFormat = function.ParamFormat;
                    tt.ActionName = function.FunctionName;
                    tt.IP = DA.Reader.GetString(2);
                    tt.Port = DA.Reader.GetInt32(3);
                    tt.HasResponse = DA.Reader.GetBoolean(4);
                    function.Actions.Add(tt);
                }
                DA.Reader.Close();
            }
            //Unknown function is only allowed to have two actions

            //*************************************************************************************

            //Read IMEI
            DA.ClearParameters();
            DA.CommandString = "SELECT ParamValue FROM General_Config WHERE ParamName=@ParamName;";
            DA.AddParameter("@ParamName", "IMEI");
            DA.ExecuteReader();
            if (DA.Reader.Read())
            {
                Global.IMEI = DA.Reader.GetString(0);
                Global.Refresh_DeviceList();
                string PortName = "";
                foreach (GSMModem modem in Global.Devices)
                {
                    if (modem.IMEINumber == Global.IMEI)
                    {
                        PortName = modem.PortName; break;
                    }
                }
                if (PortName == "")
                {
                    DA.Reader.Close();
                    DA.Close();
                    OnlyService();
                    ServiceLog.WriteEntry("Device with IMEI:" + Global.IMEI + " has not been found", EventLogEntryType.Error);
                    return;
                }
                if (!Global.DeviceManager.SMSConfig(PortName))
                {
                    DA.Reader.Close();
                    DA.Close();
                    OnlyService();
                    ServiceLog.WriteEntry("Device with IMEI:" + Global.IMEI + " is not responding. On port " + PortName, EventLogEntryType.Error);
                    return;
                }
                DA.Reader.Close();
            }
            else
            {
                DA.Reader.Close();
                DA.Close();//end as configuration not found
                OnlyService();
                ServiceLog.WriteEntry("IMEI not assigned", EventLogEntryType.Error);
                return;
            }

            //Read AllowedCountryCodes
            DA.ClearParameters();
            DA.CommandString = "SELECT ParamValue FROM General_Config WHERE ParamName=@ParamName;";
            DA.AddParameter("@ParamName", "AllowedCountryCodes");
            DA.ExecuteReader();
            if (DA.Reader.Read())
            {
                string str = DA.Reader.GetString(0);
                if (string.IsNullOrEmpty(str)) Global.AllowedCountryCodes = null;
                else
                    Global.AllowedCountryCodes = str.Split(';');
                DA.Reader.Close();
            }
            else
            {
                DA.Reader.Close();
                DA.Close();//end as configuration not found
                OnlyService();
                ServiceLog.WriteEntry("Settings incomplete. Please run SMS Server manager.", EventLogEntryType.Error);
                return;
            }
            //close the database as the database is no longer in need
            DA.Close();

                LoadServices();
                ServiceLog.WriteEntry("Loading modules....", EventLogEntryType.Information);
                return;
        }

        private void LoadServices()
        {
            dc = new Device_Controller();
            dc.StartController();
            mp = new Message_Processor();
            mp.RunProcessor();
            sc = new Service_Controller();
            sc.StartServer();
        }

        protected override void OnCustomCommand(int command)
        {
            if (command == 128)
            {
                Global.ShuttingDown = true;
                if (sc != null) sc.Close();
                if (mp != null) mp.Close();
                if (dc != null) dc.Close();
            }
        }

        protected override void OnStop()
        {
            Global.ShuttingDown = true;
            if (sc != null) sc.Close();
            if (mp != null) mp.Close();
            if (dc != null) dc.Close();
        }

    }
}
